package com.lanxiao.cloud.modules.oauth.domain.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.collect.Lists;
import com.lanxiao.cloud.common.core.enums.DelFlagEnum;
import com.lanxiao.cloud.common.core.enums.MethodEnum;
import com.lanxiao.cloud.common.core.enums.StateEnum;
import com.lanxiao.cloud.common.core.utils.StringUtils;
import com.lanxiao.cloud.common.mybatis.service.impl.BaseServiceImpl;
import com.lanxiao.cloud.common.redis.template.RedisTemplateUtils;
import com.lanxiao.cloud.common.core.exception.BusinessException;
import com.lanxiao.cloud.modules.oauth.domain.check.CheckPermissionHandle;
import com.lanxiao.cloud.modules.oauth.domain.event.PermissionChangEvent;
import com.lanxiao.cloud.modules.oauth.domain.event.PermissionChangEventPublisher;
import com.lanxiao.cloud.modules.oauth.domain.service.IPermissionService;
import com.lanxiao.cloud.modules.oauth.infrastructure.constant.OauthRedisConstant;
import com.lanxiao.cloud.common.core.error.OauthErrorType;
import com.lanxiao.cloud.modules.oauth.infrastructure.repository.PermissionMapper;
import java.util.List;
import java.util.Objects;
import com.lanxiao.cloud.modules.oauth.domain.model.Permission;
import org.springframework.stereotype.Service;

/**
 * @author ：qiaoliang
 * @date ：2021-08-25
 */
@Service
public class PermissionServiceImpl extends
    BaseServiceImpl<Long, PermissionMapper, Permission> implements
    IPermissionService {

  private final PermissionChangEventPublisher changEventPublisher;

  public PermissionServiceImpl(
      PermissionChangEventPublisher changEventPublisher) {
    this.changEventPublisher = changEventPublisher;
  }

  @Override
  public void savePermission(Permission permission) {
    this.checkRepeatPermission(null, permission.getMark(), permission.getUrl(), permission.getMethod(),
        permission.getPath());
    final Permission parent = this.getById(permission.getParentId());
    permission.fillParentIds(parent);
    this.save(permission);
  }

  @Override
  public void updatePermission(Permission permission) {
    this.checkRepeatPermission(permission.getId(), permission.getMark(), permission.getUrl(), permission.getMethod(),
        permission.getPath());
    final Permission parent = this.getById(permission.getParentId());
    permission.fillParentIds(parent);
    this.updateById(permission);
    changEventPublisher.asyncPublish(new PermissionChangEvent());
  }

  @Override
  public void deletePermission(Long id) {
    final Permission permission = this.getById(id);
    CheckPermissionHandle.init(permission).checkExist().empty();
    permission.delete();
    this.updateById(permission);
    changEventPublisher.asyncPublish(new PermissionChangEvent());
  }

  @Override
  public void updateState(Long id) {
    final Permission permission = this.getById(id);
    CheckPermissionHandle.init(permission).checkExist().empty();
    permission.updateState();
    this.updateById(permission);
    changEventPublisher.asyncPublish(new PermissionChangEvent());
  }

  @Override
  public void checkRepeatPermission(Long id, String mark, String url, MethodEnum method, String path) {
    if(StringUtils.isBlank(mark) && StringUtils.isBlank(url)
        && Objects.isNull(method) && StringUtils.isBlank(path)) {
      return;
    }
    LambdaQueryWrapper<Permission> queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper
        .ne(Objects.nonNull(id), Permission::getId, id)
        .and(StringUtils.isNotBlank(url) && Objects.nonNull(method),
            o->o.eq(Permission::getUrl, url).eq(Permission::getMethod, method))
        .eq(StringUtils.isNotBlank(path), Permission::getPath, path)
        .eq(StringUtils.isNotBlank(mark), Permission::getMark, mark)
        .ne(Permission::getDelFlag, DelFlagEnum.DELETE);

    CheckPermissionHandle.init(this.getOne(queryWrapper))
        .checkRepeatMark(mark)
        .checkRepeatPath(path)
        .checkRepeatUrl(url, method)
        .empty();
  }


  @Override
  public List<Permission> getUserPermission(Long userId) {
    final String userIdValueOf = String.valueOf(userId);
    List<Permission> permissionList = RedisTemplateUtils.hashGet(OauthRedisConstant.OAUTH_PERMISSION, userIdValueOf);
    if(Objects.isNull(permissionList)) {
      permissionList = this.baseMapper.findByUserId(userId);
      if(CollUtil.isNotEmpty(permissionList)) {
        RedisTemplateUtils.hashsSet(OauthRedisConstant.OAUTH_PERMISSION,
            userIdValueOf, permissionList, 60*60);
      }
    }
    return permissionList;
  }

  @Override
  public void checkUserPermission(Long userId, String url, MethodEnum method) {
    // 查询所有请求权限/用户所有权限，验证是否需要拦截验证
    List<Permission> list = RedisTemplateUtils.listGet(
        OauthRedisConstant.OAUTH_URL_PERMISSION);
    if(CollUtil.isEmpty(list)) {
      list = this.list(new LambdaQueryWrapper<Permission>()
          .eq(Permission::getDelFlag, DelFlagEnum.NOT_DELETE)
          .eq(Permission::getState, StateEnum.NORMAL));
      if(CollUtil.isNotEmpty(list)) {
        RedisTemplateUtils.listSet(OauthRedisConstant.OAUTH_URL_PERMISSION, list);
      }
    }
    // 校验请求地址是否需要权限
    if(CheckPermissionHandle.checkNeedAccessPermission(list, url, method)) {
      // 校验用户是否有权限访问
      BusinessException.isFalse(
          CheckPermissionHandle.checkNeedAccessPermission(this.getUserPermission(userId), url, method),
          OauthErrorType.TOKEN_NOT_PERMISSION_ERROR);
    }
  }

}
